/*
 *
 *    BLITZ Google Analytics for iPhone
 *    Copyright (C) 2009 BLITZ Digital Studios LLC d/b/a BLITZ Agency.
 *
 *    This library is free software; you can redistribute it and/or modify it 
 *    under the terms of the GNU Lesser General Public License as published
 *    by the Free Software Foundation; either version 2.1 of the License, or 
 *    (at your option) any later version.
 *
 *    This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 
 *    without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
 *    PURPOSE. See the GNU Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public License along 
 *    with this library; if not, write to the Free Software Foundation, Inc.,
 *    59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
 *
 *    BLTIZ Digital Studios LLC, 3415 S. Sepulveda BLVD, Ste 500, Los Angeles CA, 90034
 *    http://www.blitzagency.com
 *    http://labs.blitzagency.com
 *
 *        Author: Adam Venturella - aventurella@blitzagency.com
 *
 */

#import <sqlite3.h>
#import "GoogleAnalytics.h"
#import "GoogleAnalyticsObject.h"

#define kSessionCountKey               @"GoogleAnalyticsSessionCount"
#define kFirstTimeKey                  @"GooleAnalyticsFirstTime"
#define kLastTimeKey                   @"GooleAnalyticsLastTime"
#define kSessionIdKey                  @"GooleAnalyticsSessionId"

const static char * baseURL          = "http://www.google-analytics.com/__utm.gif";
const static char * analyticsVersion = "4.3";
const static char * languageEncoding = "UTF-8";
const static char * screenResolution = "320x480";
const static char * colorDepth       = "32-bit";
const static char * javaEnabled      = "0";
const static char * flashVersion     = "-";
const static char * referral         = "-";
const static char * applicationName  = "iPhone";
static int          hitId;
static int          domainHash;
static unsigned long long        sessionId;
static uint         firstTime;
static uint         lastTime;
static uint         currentTime;
static uint         sessionCount = 1; 

@interface GoogleAnalytics(Private)
-(NSString *)buildQueryStringWithEvent:(NSString *)event;
-(NSString *)generateUTMCC;
-(int)generateHash:(const char *)input;
-(unsigned long long)generateSessionId;
-(int)generateUserDataHash;
-(int)generateHitId;
-(void)initializeValuesFromDefaults;
-(void)initializeSessionCount;
-(void)initializeTimeTracking;
-(void)initializeSessionId;
-(NSError *)send:(GoogleAnalyticsObject *)GAO;
-(void)pushToGAServer:(NSTimer *)timer;
@end

@implementation GoogleAnalytics
-(void)dealloc
{
	[GoogleAnalyticsObject finalizeStatements];
	[[NSUserDefaults standardUserDefaults] setInteger:currentTime forKey:kLastTimeKey];
	[[NSUserDefaults standardUserDefaults] synchronize];
	[super dealloc];
}

#pragma mark Initializing

-(id)initWithAccount:(NSString *)aAccount andHost:(NSString *)aHost;
{
	if(self = [super init])
	{
		host       = aHost;
		account    = aAccount;
		domainHash = [self generateHash:[host UTF8String]];
		hitId      = [self generateHitId];
		[self initializeValuesFromDefaults];
	}
	
	return self;
}


-(void)setOfflineSQLStore:(sqlite3 *)db
{
	if(database){
		sqlite3_close(database);
	}
	
	database = db;
}

#pragma mark Tracking

-(void)track:(NSString *)value
{
	GoogleAnalyticsObject * obj = [[GoogleAnalyticsObject alloc] init];
	obj.value                   = [self buildQueryStringWithEvent:value];
	[self performSelectorInBackground:@selector(send:) withObject:obj];
	[obj autorelease];
}

-(NSError *)send:(GoogleAnalyticsObject *)GAO
{	
	NSAutoreleasePool * pool     = [[NSAutoreleasePool alloc] init];
	NSError           * error    = nil;
	NSURLRequest      * request  = [NSURLRequest requestWithURL:[NSURL URLWithString:GAO.value]];
	[NSURLConnection sendSynchronousRequest:request returningResponse:nil error:&error];
	
	if(error)
	{
		NSLog(@"GoogleAnalytics.m: No Network Connection Available");
		if(GAO.objectid == 0)
		{
			NSLog(@"GoogleAnalytics.m: Writing to local store.");
			[GAO insertIntoDatabase:database];
		}
	}
	else
	{
		
		if(GAO.objectid > 0)
		{
			NSLog(@"GoogleAnalytics.m: Object was in the local store and has been successfuly sent to google. Remove from the local store.");
			[GAO removeFromDatabase:database];
		}
		
		NSLog(@"GoogleAnalytics.m: Sent to Google:%@", GAO.value);
	}
	
	return error;
	
	[pool release];
}

#pragma mark Synchronizing

-(void)synchronize
{
	// first test if we have a connection:
	// but no need to couple this with Apple's Reachability
	
	NSError * error = nil;
	[NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://google.com"]] returningResponse:nil error:&error];
	
	if(!error){
		[self performSelectorInBackground:@selector(pushToGAServer:) withObject:nil];
	}
}
	
-(void)pushToGAServer:(NSTimer *)timer
{	
	NSAutoreleasePool * pool     = [[NSAutoreleasePool alloc] init];
	
	const char *sql = "SELECT objectid, value FROM analyticsobject";
	sqlite3_stmt *statement;
	
	if (sqlite3_prepare_v2(database, sql, -1, &statement, NULL) == SQLITE_OK) 
	{
		while (sqlite3_step(statement) == SQLITE_ROW) 
		{
			int objectid       = sqlite3_column_int(statement, 0);
			 const char * value = (char *)sqlite3_column_text(statement, 1);
			 
			 GoogleAnalyticsObject * obj = [[GoogleAnalyticsObject alloc] initWithPrimaryKey:objectid];
			 obj.value = [NSString stringWithUTF8String:value];
			 
			NSError * error = [self send:obj];
			[obj release];
			
			if(error)
			{
				break;
			}
			else
			{
				sleep(2);
			}
		}
	}
	
	sqlite3_finalize(statement);
	
	[pool release];
}

#pragma mark Generator

-(NSString *)buildQueryStringWithEvent:(NSString *)event
{
	return [NSString stringWithFormat:@"%s?utmvw=%s&utmn=%i&utmhn=%@&utmcs=%s&utmsr=%s&utmsc=%s&utmul=%@&utmje=%s&utmfl=%s&utmdt=%@&utmhid=%i&utmr=%s&utmp=%@&utmac=%@&utmcc=%@", baseURL,
			analyticsVersion,
            [self generateHitId],
			host,
			languageEncoding,
			screenResolution,
			colorDepth,
			[[NSLocale preferredLanguages] objectAtIndex:0],
			javaEnabled,
			flashVersion,
			@"",
			hitId,
			referral,
			event, 
			account,
			[self generateUTMCC]];
}

-(NSString *)generateUTMCC
{
	currentTime = time(NULL);
	
	//[[NSUserDefaults standardUserDefaults] setInteger:currentTime forKey:kLastTimeKey];
	//[[NSUserDefaults standardUserDefaults] synchronize];
	
	const char * utmzSuffix = "1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none);";
	NSString * utma =  [NSString stringWithFormat:@"__utma=%u.%llu.%u.%u.%u.%u;+", domainHash, sessionId, firstTime, lastTime, currentTime, sessionCount];
	NSString * utmz =  [NSString stringWithFormat:@"__utmz=%u.%u.%s", domainHash, firstTime, utmzSuffix];
	NSString * cookie = [NSString stringWithFormat:@"%@%@", utma, utmz];
	
	NSString *escaped = [(NSString*)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,  (CFStringRef)cookie, NULL,  CFSTR("?=&+;"), kCFStringEncodingUTF8) autorelease];
	
	return escaped;
}

#pragma mark Default Values

-(void)initializeValuesFromDefaults
{
	[self initializeSessionId];
	[self initializeSessionCount];
	[self initializeTimeTracking];
	[[NSUserDefaults standardUserDefaults] synchronize];
}

-(void)initializeSessionId
{
	NSNumber * result      = [[NSUserDefaults standardUserDefaults] valueForKey:kSessionIdKey];
	unsigned long long sid = [result unsignedLongLongValue];
	
	if(!sid)
	{
		sid = [self generateSessionId];
		
		NSNumber * number = [NSNumber numberWithUnsignedLongLong:sid];
		[[NSUserDefaults standardUserDefaults] setValue:number forKey:kSessionIdKey];
	}
	
	sessionId = sid;
}

-(void)initializeSessionCount
{
	int count = [[NSUserDefaults standardUserDefaults] integerForKey:kSessionCountKey];
	
	if(count){
		sessionCount = (uint) count + 1;
	}
	
	[[NSUserDefaults standardUserDefaults] setInteger:sessionCount forKey:kSessionCountKey];
}

-(void)initializeTimeTracking
{
	uint ft  = (uint)[[NSUserDefaults standardUserDefaults] integerForKey:kFirstTimeKey];
	uint lt  = (uint)[[NSUserDefaults standardUserDefaults] integerForKey:kLastTimeKey];
	
	if(!ft)
	{
		ft = time(NULL);
		lt = ft;
	}
	
	firstTime = ft;
	lastTime  = lt;
	
	[[NSUserDefaults standardUserDefaults] setInteger:firstTime forKey:kFirstTimeKey];
	[[NSUserDefaults standardUserDefaults] setInteger:lastTime forKey:kLastTimeKey];
	
}

#pragma mark gaforflash
-(unsigned long long)generateSessionId
{
	srandom(time(NULL));
	unsigned long long sid = ((unsigned long long)random() ^ (unsigned long long)[self generateUserDataHash]) * RAND_MAX;
	return sid;
}

-(int)generateHitId
{
	srandom(time(NULL));
	return random();
}

-(int)generateUserDataHash
{
	//appName
	//appVersion
	//language
	//userAgent - NOT USED
	//platform
	//dimensions
	//colorDepth
	//referrer - NOT USED
	
	NSString * hash = [NSString stringWithFormat:@"%s%@%@%@%s%s%@", applicationName, 
															[[UIDevice currentDevice] systemVersion],
															[[NSLocale preferredLanguages] objectAtIndex:0],
															[[UIDevice currentDevice] systemName],
															screenResolution,
															colorDepth,@""];
	
	return [self generateHash:[hash UTF8String]];
}

-(int)generateHash:(const char *)input
{
	int hash      = 1;
	int leftMost7 = 0;
	int pos;
	int current;
	
	if(input && input != "")
	{
		hash = 0;
		
		for (pos = strlen(input) - 1; pos >= 0; pos--)
		{
			current   = input[pos];
			hash      = ((hash << 6) & 0xfffffff) + current + (current << 14);
			
			leftMost7 = hash & 0xfe00000;
			
			if(leftMost7 != 0){
				hash ^= leftMost7 >> 21;
			}
		}
	}
	
	return hash;
}
@end
